<template>
  <span class="animated-number">
    {{ formatNumber(displayValue) }}
  </span>
</template>

<script setup lang="ts">
import { ref, watch, onMounted } from 'vue'

interface Props {
  value: number
  decimals?: number
  duration?: number
  prefix?: string
  suffix?: string
}

const props = withDefaults(defineProps<Props>(), {
  decimals: 2,
  duration: 2000,
  prefix: '$',
  suffix: ''
})

const displayValue = ref(0)
let animationId: number | null = null

const formatNumber = (num: number): string => {
  const formatted = num.toFixed(props.decimals)
  const parts = formatted.split('.')

  // 添加千分位分隔符
  parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',')

  return `${props.prefix}${parts.join('.')}${props.suffix}`
}

const animateToValue = (targetValue: number) => {
  if (animationId) {
    cancelAnimationFrame(animationId)
  }

  const startValue = displayValue.value
  const difference = targetValue - startValue
  const startTime = performance.now()

  const animate = (currentTime: number) => {
    const elapsedTime = currentTime - startTime
    const progress = Math.min(elapsedTime / props.duration, 1)

    // 使用 easeOutCubic 缓动函数
    const easeProgress = 1 - Math.pow(1 - progress, 3)

    displayValue.value = startValue + (difference * easeProgress)

    if (progress < 1) {
      animationId = requestAnimationFrame(animate)
    } else {
      displayValue.value = targetValue
      animationId = null
    }
  }

  animationId = requestAnimationFrame(animate)
}

watch(() => props.value, (newValue) => {
  if (newValue !== displayValue.value) {
    animateToValue(newValue)
  }
}, { immediate: true })

onMounted(() => {
  if (props.value > 0) {
    animateToValue(props.value)
  }
})
</script>

<style scoped lang="scss">
.animated-number {
  font-variant-numeric: tabular-nums;
  transition: color 0.3s ease;

  &:hover {
    color: #7c3aed;
  }
}
</style>